#include "convolution.hh"

ApplyConvolution::ApplyConvolution (IplImage* input_,
				    int ul_,
				    int uc_,
				    int ur_,
				    int ml_,
				    int mc_,
				    int mr_,
				    int bl_,
				    int bc_,
				    int br_)
  : input(input_),
    ul(ul_),
    uc(uc_),
    ur(ur_),
    ml(ml_),
    mc(mc_),
    mr(mr_),
    bl(bl_),
    bc(bc_),
    br(br_)
{
  output = cvCreateImage (cvSize (input->width, input->height), IPL_DEPTH_8U, 3); 
}

void ApplyConvolution::operator() (const tbb::blocked_range2d<int>& r) const
{
  for (int i = r.rows().begin(); i!= r.rows().end(); ++i)
    for (int j = r.cols().begin(); j!= r.cols().end(); ++j)
      {
	CvScalar s = cvScalarAll (0);
	CvScalar sul = cvScalarAll(0);
	CvScalar suc = cvScalarAll(0);
	CvScalar sur = cvScalarAll(0);
	CvScalar sml = cvScalarAll(0);
	CvScalar smr = cvScalarAll(0);
	CvScalar sbl = cvScalarAll(0);
	CvScalar sbc = cvScalarAll(0);
	CvScalar sbr = cvScalarAll(0);

	if (i - 1 >= 0 && j - 1 >= 0)
	  {
	    sul = cvGet2D (input, i - 1, j - 1);
	    //std::cout << "for " << i << ", " << j << " sul is now " << sul.val[0] << std::endl;
	  }
	if (i - 1 >= 0)
	  suc = cvGet2D (input, i - 1, j);
	if (i - 1 >= 0 && j + 1 < input->width)
	  sur = cvGet2D (input, i - 1, j + 1);

	if (j - 1 >= 0)
	  sml = cvGet2D (input, i, j - 1);
	CvScalar smc = cvGet2D (input, i, j);
	//	  std::cout << smc.val[1] << std::endl;
	if (j + 1 < input->width)
	  smr = cvGet2D (input, i, j + 1);

	if (i + 1 < input->height && j - 1 >= 0)
	  sbl = cvGet2D (input, i + 1, j - 1);
	if (i + 1 < input->height)
	  sbc = cvGet2D (input, i + 1, j);
	if (i + 1 < input->height && j + 1 < input->width)
	  sbr = cvGet2D (input, i + 1, j + 1);

	//	  std::cout << sul.val[0] << std::endl;

	s.val[0] = (ul * sul.val[0] +
		    uc * suc.val[0] +
		    ur * sur.val[0] +
		    ml * sml.val[0] +
		    mc * smc.val[0] +
		    mr * smr.val[0] +
		    bl * sbl.val[0] +
		    bc * sbc.val[0] +
		    br * sbr.val[0]) / 100;
	s.val[1] = (ul * sul.val[1] +
		    uc * suc.val[1] +
		    ur * sur.val[1] +
		    ml * sml.val[1] +
		    mc * smc.val[1] +
		    mr * smr.val[1] +
		    bl * sbl.val[1] +
		    bc * sbc.val[1] +
		    br * sbr.val[1]) / 100;
	s.val[2] = (ul * sul.val[2] +
		    uc * suc.val[2] +
		    ur * sur.val[2] +
		    ml * sml.val[2] +
		    mc * smc.val[2] +
		    mr * smr.val[2] +
		    bl * sbl.val[2] +
		    bc * sbc.val[2] +
		    br * sbr.val[2]) / 100;
	cvSet2D (output, i, j, s);
      }
}

IplImage*
ApplyConvolution::getOutput ()
{
  return output;
}

IplImage*
convolution (IplImage* input,
	     int ul,
	     int uc,
	     int ur,
	     int ml,
	     int mc,
	     int mr,
	     int bl,
	     int bc,
	     int br)
{
  ApplyConvolution app(input, ul, uc, ur, ml, mc, mr, bl, bc, br);
  tbb::parallel_for(tbb::blocked_range2d<int>(0,input->height, 0,input->width),
                    app, tbb::auto_partitioner());

  return app.getOutput ();
}
